package service

import (
	"context"
	"encoding/json"
	"k8s-dash/dto"
	"k8s-dash/utils"
	appsV1 "k8s.io/api/apps/v1"
	coreV1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"strconv"
	"time"
)

type deployment struct{}

var Deployment deployment

type DeployResp struct {
	Total int                 `json:"total"`
	Items []appsV1.Deployment `json:"items"`
}
type DeployToNS struct {
	Namespace string `json:"namespaces"`
	DeployNum int    `json:"deploy_num"`
}

func (p *deployment) CreateDeploy(input *dto.DeployCreateInput) error {
	//初始化一个deploy对象出来
	deployment := &appsV1.Deployment{
		ObjectMeta: metaV1.ObjectMeta{
			Name:      input.Name,
			Namespace: input.NameSpace,
			Labels:    input.Labels,
		},
		Spec: appsV1.DeploymentSpec{
			Replicas: &input.Replicas,
			Selector: &metaV1.LabelSelector{
				MatchLabels:      input.Labels,
				MatchExpressions: nil,
			},
			Template: coreV1.PodTemplateSpec{
				ObjectMeta: metaV1.ObjectMeta{
					Name:   input.Name,
					Labels: input.Labels,
				},
				Spec: coreV1.PodSpec{
					Containers: []coreV1.Container{
						{
							Name:  input.Name,
							Image: input.Image,
							Ports: []coreV1.ContainerPort{
								{
									Name:          "http",
									Protocol:      coreV1.ProtocolTCP,
									ContainerPort: input.ContainerPort,
								},
							},
						},
					},
				},
			},
		},
		Status: appsV1.DeploymentStatus{},
	}
	//健康检查接口
	if input.HealthCheck {
		deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &coreV1.Probe{
			ProbeHandler: coreV1.ProbeHandler{
				HTTPGet: &coreV1.HTTPGetAction{
					Path: input.HealthPath,
					Port: intstr.IntOrString{
						Type:   0,
						IntVal: input.ContainerPort,
					},
					Host:        "",
					Scheme:      "",
					HTTPHeaders: nil,
				},
			},
			InitialDelaySeconds: 10,
			TimeoutSeconds:      10,
			PeriodSeconds:       10,
		}
		deployment.Spec.Template.Spec.Containers[0].LivenessProbe = &coreV1.Probe{
			ProbeHandler: coreV1.ProbeHandler{
				HTTPGet: &coreV1.HTTPGetAction{
					Path: input.HealthPath,
					Port: intstr.IntOrString{
						Type:   0,
						IntVal: input.ContainerPort,
					},
					Host:        "",
					Scheme:      "",
					HTTPHeaders: nil,
				},
			},
			InitialDelaySeconds: 10,
			TimeoutSeconds:      10,
			PeriodSeconds:       10,
		}
	}
	//deploy的资源限制
	deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[coreV1.ResourceName]resource.Quantity{
		coreV1.ResourceCPU:    resource.MustParse(input.Cpu),
		coreV1.ResourceMemory: resource.MustParse(input.Memory),
	}
	deployment.Spec.Template.Spec.Containers[0].Resources.Requests = map[coreV1.ResourceName]resource.Quantity{
		coreV1.ResourceCPU:    resource.MustParse(input.Cpu),
		coreV1.ResourceMemory: resource.MustParse(input.Memory),
	}
	//通过sdk创建deploy
	if _, err := utils.K8sClient.K8sClient.AppsV1().Deployments(input.NameSpace).Create(context.TODO(), deployment, metaV1.CreateOptions{}); err != nil {
		return err
	}
	return nil
}

// GetDeployList 获取deploy列表
func (p *deployment) GetDeployList(input *dto.DeployListInput) (resp *DeployResp, err error) {
	deployList, err := utils.K8sClient.K8sClient.AppsV1().Deployments(input.Namespace).List(context.TODO(), metaV1.ListOptions{})
	if err != nil {
		return nil, err
	}
	//清洗数据
	selectedData := &dataSelector{
		GenericDataList: p.toCells(deployList.Items),
		DataSelectQuery: &DataSelectorQuery{
			Filter:   &FilterQuery{input.FilterName},
			Paginate: &PageQuery{input.Limit, input.Page},
		},
	}
	//先过滤
	sd := selectedData.Filter()
	total := len(sd.GenericDataList)
	//排序
	data := sd.Sort().Paginate()
	//再将数据装换为appsV1.Deployment
	ds := p.FromCells(data.GenericDataList)
	//返回数据
	return &DeployResp{
		Total: total,
		Items: ds,
	}, nil
}

// GetDeployDetail 获取deploy详情
func (p *deployment) GetDeployDetail(data *dto.DeployNameInput) (*appsV1.Deployment, error) {
	deploy, err := utils.K8sClient.K8sClient.AppsV1().Deployments(data.Namespace).Get(context.TODO(), data.DeployName, metaV1.GetOptions{})
	if err != nil {
		return nil, err
	}
	return deploy, nil
}

// RestartDeploy 触发重启机制
func (p *deployment) RestartDeploy(deployName, namespace string) error {
	//随便改一个值就会触发重启
	patchData := map[string]interface{}{
		"spec": map[string]interface{}{
			"template": map[string]interface{}{
				"spec": map[string]interface{}{
					"containers": []map[string]interface{}{
						{
							"name": deployName,
							"env": []map[string]interface{}{
								{
									"name":  "RESTART_",
									"value": strconv.FormatInt(time.Now().Unix(), 10),
								},
							},
						},
					},
				},
			},
		},
	}
	//序列化
	bytes, err := json.Marshal(patchData)
	if err != nil {
		return err
	}
	//调用sdk更新
	if _, err := utils.K8sClient.K8sClient.AppsV1().Deployments(namespace).Patch(context.TODO(), deployName, "application/strategic-merge-patch+json", bytes, metaV1.PatchOptions{}); err != nil {
		return err
	}
	return nil
}

// ScaleDeploy 扩缩容副本
func (p *deployment) ScaleDeploy(deployName, namespace string, scaleNum int) (int32, error) {
	scale, err := utils.K8sClient.K8sClient.AppsV1().Deployments(namespace).GetScale(context.TODO(), deployName, metaV1.GetOptions{})
	if err != nil {
		return 0, err
	}
	scale.Spec.Replicas = int32(scaleNum)
	updateScale, err := utils.K8sClient.K8sClient.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deployName, scale, metaV1.UpdateOptions{})
	if err != nil {
		return 0, err
	}
	return updateScale.Status.Replicas, nil
}

// DeleteDeploy 删除deploy
func (p *deployment) DeleteDeploy(deployName, namespace string) error {
	return utils.K8sClient.K8sClient.AppsV1().Deployments(namespace).Delete(context.TODO(), deployName, metaV1.DeleteOptions{})
}

// UpdateDeploy 更新pod
func (p *deployment) UpdateDeploy(namespace, content string) error {
	var deploy = &appsV1.Deployment{}
	//将content反序列化为pod
	err := json.Unmarshal([]byte(content), deploy)
	if err != nil {
		return err
	}
	if _, err := utils.K8sClient.K8sClient.AppsV1().Deployments(namespace).Update(context.TODO(), deploy, metaV1.UpdateOptions{}); err != nil {
		return err
	}
	return nil
}

// GetDeployToNS 获取对应namespace下deploy的数量，这个功能跟podList功能重复，这个是获取所有ns分别对应的pod数量
func (p *deployment) GetDeployToNS() (DENS []*DeployToNS, err error) {
	//首先获取所有的ns
	namespaceList, err := utils.K8sClient.K8sClient.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{})
	if err != nil {
		return nil, err
	}
	for _, namespace := range namespaceList.Items {
		deployList, err := utils.K8sClient.K8sClient.AppsV1().Deployments(namespace.Name).List(context.TODO(), metaV1.ListOptions{})
		if err != nil {
			return nil, err
		}
		//组装数据
		dns := &DeployToNS{
			Namespace: namespace.Name,
			DeployNum: len(deployList.Items),
		}
		DENS = append(DENS, dns)
	}
	return DENS, nil
}

// 类型转换的方法 coreV1.pod => DataCell,DataCell => coreV1.pod
func (p *deployment) toCells(ds []appsV1.Deployment) []DataCell {
	cells := make([]DataCell, len(ds))
	for i := range ds {
		cells[i] = deployCell(ds[i])
	}
	return cells
}

func (p *deployment) FromCells(cells []DataCell) []appsV1.Deployment {
	ds := make([]appsV1.Deployment, len(cells))
	for i := range cells {
		ds[i] = appsV1.Deployment(cells[i].(deployCell))
	}
	return ds
}
